﻿using System.Collections.Generic;
using PerfAssist.MemoryProfilerAdvanced.Editor.PAContrib;
using UnityEditor;
using UnityEngine;

namespace PerfAssist.Common.Editor
{
    public class ShaderItem
    {
        public string ShaderName = "shader";
        public int ShaderRefCount;
    }

    public class MaterialItem
    {
        public string MatName = "mat_name";
        public Material MatObj;
        public string MatPath = "mat_path";
    }

    public class PAShaderCleanupWindow : EditorWindow
    {
        private TableView.TableView _matList;

        private readonly Dictionary<string, List<Material>> _otherMaterials = new Dictionary<string, List<Material>>();
        private TableView.TableView _otherShaderRefTable;

        private readonly Dictionary<Shader, int> _shaderDict = new Dictionary<Shader, int>();

        private TableView.TableView _shaderRefTable;

        [MenuItem(PAEditorConst.MenuPath + "/Shader 清理和优化")]
        private static void Create()
        {
            var w = GetWindow<PAShaderCleanupWindow>();
            if (w.GetType().Name == "PAShaderCleanupWindow")
            {
                w.minSize = new Vector2(800, 600);
                w.Show();
            }
        }

        private void Awake()
        {
            _shaderRefTable = new TableView.TableView(this, typeof(ShaderItem));
            _shaderRefTable.AddColumn("ShaderName", "ShaderName", 0.8f, TextAnchor.MiddleLeft);
            _shaderRefTable.AddColumn("ShaderRefCount", "RefCount", 0.2f);
            _shaderRefTable.OnSelected += TableView_ShaderSelected;

            _otherShaderRefTable = new TableView.TableView(this, typeof(ShaderItem));
            _otherShaderRefTable.AddColumn("ShaderName", "ShaderName", 0.8f, TextAnchor.MiddleLeft);
            _otherShaderRefTable.AddColumn("ShaderRefCount", "RefCount", 0.2f);
            _otherShaderRefTable.OnSelected += TableView_ShaderSelected;

            _matList = new TableView.TableView(this, typeof(MaterialItem));
            _matList.AddColumn("MatName", "Name", 0.3f, TextAnchor.MiddleLeft);
            _matList.AddColumn("MatPath", "Path", 0.7f, TextAnchor.MiddleLeft);
            _matList.OnSelected += TableView_MaterialSelected;

            RefreshTables();
        }

        private void RefreshTables()
        {
            Debug.LogFormat("Looking for shaders...");
            var shaders = PAAssetUtil.FindByType<Shader>();
            Debug.LogFormat("{0} shaders found.", shaders.Count);

            Debug.LogFormat("Looking for materials...");
            var materials = PAAssetUtil.FindByType<Material>();
            Debug.LogFormat("{0} materials found.", materials.Count);

            _shaderDict.Clear();
            foreach (var s in shaders)
                _shaderDict[s] = 0;

            var otherShaderDict = new Dictionary<Shader, int>();

            foreach (var m in materials)
            {
                if (_shaderDict.ContainsKey(m.shader))
                {
                    _shaderDict[m.shader]++;
                }
                else
                {
                    if (otherShaderDict.ContainsKey(m.shader))
                        otherShaderDict[m.shader]++;
                    else
                        otherShaderDict[m.shader] = 1;
                }

                if (!_otherMaterials.ContainsKey(m.shader.name)) _otherMaterials.Add(m.shader.name, new List<Material>());
                _otherMaterials[m.shader.name].Add(m);
            }

            var entries = new List<object>();
            foreach (var p in _shaderDict)
            {
                var si = new ShaderItem();
                si.ShaderName = p.Key.name;
                si.ShaderRefCount = p.Value;
                entries.Add(si);
            }

            _shaderRefTable.RefreshData(entries);

            var otherEntries = new List<object>();
            foreach (var p in otherShaderDict)
            {
                var si = new ShaderItem();
                si.ShaderName = p.Key.name;
                si.ShaderRefCount = p.Value;
                otherEntries.Add(si);
            }

            _otherShaderRefTable.RefreshData(otherEntries);

            _matList.RefreshData(null);
        }

        private void DrawTable(TableView.TableView table, Rect rect)
        {
            if (table != null)
            {
                GUILayout.BeginArea(rect);
                table.Draw(new Rect(0, 0, rect.width, rect.height));
                GUILayout.EndArea();
            }
        }

        private void OnGUI()
        {
            GUILayout.BeginVertical();

            GUILayout.BeginHorizontal(MemStyles.Toolbar);
            if (GUILayout.Button("Refresh", MemStyles.ToolbarButton)) RefreshTables();
            GUILayout.Space(10);
            if (GUILayout.Button("Find all unused 'jx3Art' shaders", MemStyles.ToolbarButton)) FindAllUnusedJx3ArtShaders();
            GUILayout.FlexibleSpace();
            GUILayout.EndHorizontal();

            var toolbar = 30.0f;
            var padding = 30.0f;

            var halfWidth = Mathf.Floor(position.width * 0.5f);
            var halfHeight = Mathf.Floor((position.height - toolbar) * 0.5f);

            var tableWidth = halfWidth - padding * 1.5f;
            var tableHeight = halfHeight - padding * 1.5f;

            DrawTable(_shaderRefTable, new Rect(padding, toolbar + padding, tableWidth,
                position.height - padding * 2.0f - toolbar));
            DrawTable(_otherShaderRefTable, new Rect(halfWidth + padding * 0.5f, toolbar + padding,
                tableWidth, tableHeight));
            DrawTable(_matList, new Rect(halfWidth + padding * 0.5f, toolbar + halfHeight + padding * 0.5f,
                tableWidth, tableHeight));

            GUILayout.EndVertical();
        }

        private void TableView_ShaderSelected(object selected, int col)
        {
            var foo = selected as ShaderItem;
            if (foo == null)
            {
                Debug.LogErrorFormat("the selected object is not a valid one. ({0} expected, {1} got)",
                    typeof(ShaderItem), selected.GetType());
                return;
            }

            List<Material> materials = null;
            if (_otherMaterials.TryGetValue(foo.ShaderName, out materials))
            {
                var matEntries = new List<object>();
                foreach (var m in materials)
                {
                    var si = new MaterialItem();
                    si.MatName = m.name;
                    si.MatPath = AssetDatabase.GetAssetPath(m);
                    si.MatObj = m;
                    matEntries.Add(si);
                }

                _matList.RefreshData(matEntries);
            }
            else
            {
                _matList.RefreshData(null);
            }
        }

        private void TableView_MaterialSelected(object selected, int col)
        {
            var foo = selected as MaterialItem;
            if (foo == null)
            {
                Debug.LogErrorFormat("the selected object is not a valid one. ({0} expected, {1} got)",
                    typeof(MaterialItem), selected.GetType());
                return;
            }

            Debug.LogFormat("mat selected. ({0}, {1})", foo.MatName, foo.MatPath);
            EditorGUIUtility.PingObject(foo.MatObj);
        }

        private void OnDestroy()
        {
            if (_shaderRefTable != null)
                _shaderRefTable.Dispose();
            if (_otherShaderRefTable != null)
                _otherShaderRefTable.Dispose();
            if (_matList != null)
                _matList.Dispose();

            _shaderRefTable = null;
            _otherShaderRefTable = null;
            _matList = null;
        }

        private void FindAllUnusedJx3ArtShaders()
        {
            var unused = new List<Shader>();
            var androidSet = new HashSet<string>();
            foreach (var p in _shaderDict)
                if (p.Key.name.StartsWith("jx3Art/") && p.Value == 0)
                    unused.Add(p.Key);
                else if (p.Key.name.StartsWith("android/jx3Art/")) androidSet.Add(p.Key.name);

            var movedSafely = new List<Shader>();
            var kept = new List<Shader>();
            foreach (var s in unused)
                if (androidSet.Contains("android/" + s.name))
                    movedSafely.Add(s);
                else
                    kept.Add(s);

            Debug.LogWarningFormat("===== Kept ===== {0}", kept.Count);
            foreach (var item in kept)
                Debug.Log(AssetDatabase.GetAssetPath(item));

            Debug.LogWarningFormat("===== Moved ===== {0}", movedSafely.Count);
            foreach (var item in movedSafely)
                Debug.Log(AssetDatabase.GetAssetPath(item));
        }
    }
}